<?php
// +----------------------------------------------------------------------
// |  帐户控制器
// +----------------------------------------------------------------------
namespace app\sucai\controller\soft;
use app\app\Appbase;
use \Firebase\JWT\JWT;
use think\facade\Cache;
use lib\wechat\Wechat;
use base\service\MsgApi;

class Auth extends Appbase
{
	private $name = 'user';   		// 用户表名-全称
	private $key = 'id';		 			// 用户guid
	private $code_field = 'phone'; // 登录账号字段
	private $pwd_field = 'password';		// 登录密码字段
	private $strKey = 'Mhc(jk`[t.7?P_Ty=A%41o+S{J390DKpFmvW@E}8';  //密钥
	private $accessTokenExpirationTime = 3600;			//accessToken过期时间(秒)
   	private $refreshTokenExpirationTime = 86400 * 5;	//refreshToken过期时间(秒)
   	
   	//-----
	//公众号
	//-----
	private $webConfig;  	
	private $appid;         
	private $appsecret;     
	private $redirect_uri;
	private $success_uri;  	
	private $scope='snsapi_userinfo';  	//snsapi_userinfo , snsapi_base
	
	//-----
	//小程序 wx-app
	//-----
	private $appConfig;      
	private $wxapp_appid;      
	private $wxapp_appsecrt;    
	
	// 用户信息关联表
	private $withs = [];
	
	//会在new的时候自动执行
	public function __construct() {
		parent::__construct();
		//公众号
		//$this->webConfig = $webConfig =  Config("wechat.web");
		$this->webConfig = $webConfig = env('system_base_config_wx_web'); 
        $this->appid = $webConfig['appid'];
        $this->appsecret = $webConfig['appsecret'];
        $this->redirect_uri = APP_PATH.'/index.php/sucai/soft/auth/getWxUserInfo';
        
        $successUri = "/h5Sucai/index.html";
        if( strstr($successUri,"http") ){
        	$this->success_uri = $successUri.(strstr($successUri,"#/") ? '' : '#/');
        }else{
        	$successUri = substr($successUri,0,1) == '/' ? substr($successUri,1) : $successUri;	
        	$this->success_uri = APP_PATH.$successUri.( strstr($successUri,"#/") ? '' : '#/');
        }
        
		//小程序
        //$this->appConfig = $appConfig =  Config("wechat.app");
		$this->appConfig = $appConfig = [
		  'appid' => env('system_base_config_wx_web')['applets_appid'],
		  'appsecret' => env('system_base_config_wx_web')['applets_appsecret']
		];
        $this->wxapp_appid = $appConfig['appid'];
        $this->wxapp_appsecrt = $appConfig['appsecret'];
        
		// 用户信息关联表
		$this->withs = [
			'shopInfo'=>function ( $query){
				$query->field('id,uid,status,status_desc,is_enable');
			},
			'vipInfo.levelInfo',
		];	
    }
	
	/**
     * 获取示例
     * @param array $options 实例配置
     * @return static
     */
    protected static $instance = null;
    public static function instance($options = [])
    {
        if (is_null(self::$instance)) {
            self::$instance = new self($options);
        }
        return self::$instance;
    }
	
	/**
     * 微信公众号入口
	 * /app/soft/auth/wxIndex
	 * @is_need_login false
	 *  路由重置： wx/inlet 
    */
	public function wxIndex($wxInfo=[]){
	    if(!$this->webConfig){
            echo "微信公众号配置信息错误，请联系后台管理员！";
            die;
        }  
		$scene = request()->get('scene',''); $path = urldecode(request()->get('path',''));
		if( empty($wxInfo) ){
			$weObj = new Wechat( $this->webConfig );
			$url = $this->redirect_uri.'?scene='.$scene.'&path='.urlencode($path);
			$url =  $weObj->getOauthRedirect($url,'',$this->scope);
			$this->redirect($url, 302);// tp5的重定向方法 
		}else{
			$wxInfo['remark'] = 'wx-web';
			$result = $this->wxLogin($wxInfo);
			if(!$result){
				return $this->ReError();
			}
			$result = $this->results($result);
			$token = 'access_token='.$result['access_token'].'&refresh_token='.$result['refresh_token'];
			//登录成功-跳转到对应网页
			if($path){
				$url = $path.( strstr($path,"?") ? '&' : '?' ).$token;
			}else{
				$url = $this->success_uri. '?'.$token.'&scUid=';
			}
	        $this->redirect($url, 302);// tp5的重定向方法 
		}
	}
	
	/**
    * 微信用户信息
	* /app/soft/auth/getWxUserInfo
	* @is_need_login false
	* 
    */
	public function getWxUserInfo()
	{
		$weObj = new Wechat( $this->webConfig );
		$result = $weObj->getOauthAccessToken();
		$wxUserinfo = Cache::get('wx_oauth_userinfo'.request()->get('code'));
		if(!$result && $wxUserinfo){
			$this->wxIndex( $wxUserinfo );
			return true;
		}
		if($result && $result['scope'] != 'snsapi_base'){
			$result = $weObj->getOauthUserinfo($result['access_token'],$result['openid']);
			if($result){
				Cache::set('wx_oauth_userinfo'.request()->get('code'),$result,360);
			}
		}
		if(!$result){
			echo "授权错误！".'</br>';
        	echo 'errcode：'.$weObj->errCode.'</br>';
        	echo 'errmsg ：'.$weObj->errMsg.'</br>';
            die;
		}
		$this->wxIndex( $result );
	}
	
	/**
     * 登录
	 * /app/soft/auth/login
	 * @is_need_login false
	 * @access public
     * @param string $user_name   	用户名称
     * @param string $user_pwd 		用户密码
     * @return json
    */
    public function login(int $type,String $data,String $code)
    {
    	$userInfo = [];
		$param =  json_decode($data,true);
		$dbuser = model($this->name);
		$codeField =  $this->code_field;
		$pwdField =  $this->pwd_field;
    	//类型1
		switch($type){
		    case 1://手机号验证码登录
				if(!$param['code'] || !$param['phone']){
					return $this->ReError("请求参数错误！");
				}
				if( !MsgApi::verifyCode($param['phone'],$param['code']) ){
					return $this->ReError("验证码不正确！");
				}
				$userInfo = $this->phoneLogin($param['phone'],$param['identity']);
	            break;
		    case 2://微信app,微信小程序登录
				if(!$param['avatarUrl'] || !$param['nickName']){
					return $this->ReError("请求参数错误！");
				}
	          	$userInfo = $this->wxLogin($param,$code ? 'wx_app' : 'wx',$code);
	            break; 
			case 3://qq登录
				if(!$param['openId'] || !$param['avatarUrl'] || !$param['nickName']){
					return $this->ReError("请求参数错误！");
				}
	          	$userInfo = $this->qqLogin($param,'qq');
	            break;
			case 4://账号密码登录
				if(!$param[$codeField] || !$param[$pwdField]){
					return $this->ReError("请求参数错误！");
				}
				$map[$codeField] = $param($codeField);
				$map[$pwdField] = md5($param($pwdField));
				$userInfo = $dbuser->where($map)->field($pwdField,true)->find();
				if( $userInfo == null ){
					return  $this->ReError('账号和密码不符');
				}
	            break; 	    
		}
    	
		if( $userInfo['is_enable'] == "0" ){
			return  $this->ReError('该账户已被禁用');
		}
		
		/*登录成功-更新对应信息*/
		$dbuser->save(['land_time'=>date("Y-m-d H:i:s")],['id'=>$userInfo["id"]]);
		
		return $this->ReSucess($this->results($userInfo),"登录成功!");
    }
	
	/**
     * 手机号验证码登录
	 * @is_need_login false
	 * /app/soft/auth/phoneLogin
	 * @access public
     * @param string $wxInfo   	
     * @return json
    */
    private function phoneLogin($phone,$identity=0){
    	//参数
    	$wxInfo['land_type'] = 'phone';
    	$wxInfo['nickname'] = $phone;
    	$wxInfo['phone'] = $phone;
    	$wxInfo['identity'] = $identity;
		//数据	
		$dbuser = model($this->name);	
        if( $userInfo = $dbuser->where("phone", $phone )->find() ) {
        	$dbuser->updateInfo($wxInfo,['phone'=>$phone]);
        }else{
			$userInfo = $dbuser->addUser($wxInfo);
			$userInfo = $dbuser->where("phone", $phone )->find();
			$userInfo['is_first_logon'] = true; 
        }
		//返回
		return empty($userInfo) ? null : $userInfo;
    }
	
	
	/**
     * 微信登录
	 * @is_need_login false
	 * /app/soft/auth/wxLogin
	 * @access public
     * @param string $wxInfo   	
     * @return json
    */
    public function wxLogin($wxInfo,$landType='wx',$code=''){
    	//参数
    	$wxInfo['region'] = $wxInfo['province'].( $wxInfo['city'] ? ','.$wxInfo['city'] : '');
    	$wxInfo['land_type'] = $landType;
    	$wxInfo['nickname'] = $wxInfo['nickname'] ?: $wxInfo['nickName'];
    	$wxInfo['unionid'] = ($wxInfo['unionid'] ?: $wxInfo['unionId']);
    	$wxInfo['openid'] = $wxInfo['openid'] ?: $wxInfo['openId'];
    	$wxInfo['sex'] =$wxInfo['sex'] ?:  ($wxInfo['gender'] == 0 ? '女' : '男');
    	$wxInfo['avatarurl'] = $wxInfo['avatarurl'] ?: $wxInfo['headimgurl'];
    	
		//小程序登录-获取openid
		if($code || !$wxInfo['openid']){
		    if(!$this->appConfig){
                echo "微信小程序配置信息错误，请联系后台管理员！";
                die;
            } 
			$appConfig =  $this->appConfig;
	        $wxapp_appid = $appConfig['appid'];
	        $wxapp_appsecrt = $appConfig['appsecret'];
			$url = "https://api.weixin.qq.com/sns/jscode2session?appid=" . $wxapp_appid . "&secret=" . $wxapp_appsecrt . "&js_code=" . $code . "&grant_type=authorization_code"; 
		    $rjson = json_decode(Wechat::http_get($url), true);
			if( !empty($rjson['errmsg']) ){
				return $this->ReError( $rjson['errmsg'] ,-1);
			}
			$wxInfo['openid'] = $rjson['openid'];
			$wxInfo['unionid'] = $rjson['unionid'];
		}
		
		//数据	
		$dbuser = model($this->name);	
		if( $wxInfo['unionid'] ) {
			if($userInfo = $dbuser->with($this->withs)->where("unionid",$wxInfo["unionid"] )->find()){
				$dbuser->updateInfo($wxInfo,['unionid'=>$userInfo["unionid"]]);
			}
        } else if( $wxInfo["openid"] ) {
        	if($userInfo = $dbuser->with($this->withs)->where("openid",$wxInfo["openid"] )->find() ){
        		unset($wxInfo['openid']);
        		$dbuser->updateInfo($wxInfo,['openid'=>$userInfo["openid"]]);
        	}
        } 
        if(!$userInfo)  $userInfo = $dbuser->addUser($wxInfo);
        
		//返回
		return empty($userInfo) ? null : $userInfo;
    }

	
	/**
     * QQ登录
	 * @is_need_login false
	 * /app/soft/auth/qqLogin
	 * @access public
     * @param string $qqInfo   	
     * @return json
    */
    private function qqLogin($qqInfo,$landType='qq',$is=true){
    	//参数
    	$qqInfo['region'] = $qqInfo['province'].','.$qqInfo['city'];
    	$qqInfo['land_type'] = $landType;
    	$qqInfo['nickname'] = $qqInfo['nickname'] ?: $qqInfo['nickName'];
    	$qqInfo['unionid'] = $qqInfo['unionid'] ?: $qqInfo['unionId'];
    	$qqInfo['openid'] = $qqInfo['openid'] ?: $qqInfo['openId'];
    	$qqInfo['sex'] = $qqInfo['gender'] ?: '保密' ;
    	$qqInfo['avatarUrl'] = $qqInfo['figureurl_2'] ?: $qqInfo['headimgurl'];
		
		//数据	
		$dbuser = model($this->name);	
        if( $userInfo = $dbuser->where("openid", $qqInfo["openid"] )->find() ) {
        	$dbuser->updateInfo($qqInfo,['openid'=>$qqInfo["openid"]]);
        }else{
			$userInfo = $dbuser->addUser($qqInfo);
			$userInfo['is_first_logon'] = true; 
        }
		//返回
		return empty($userInfo) ? null : $userInfo;
    }
	
	/**
    * 退出
	* /app/soft/auth/logout
	* @is_need_login false
    * @return boolean
    */
    public function logout($is=false)
    {
        return  json(array("code"=>0,"data"=>"","msg"=>"退出成功"));
    }
	
	/**
     * 获取用户信息
	 * /app/soft/auth/getUserInfo
	 * @is_need_login true
     * @return boolean
     */
    public function getUserInfo($token)
    {
    	if( empty($token) == true ){
			throw new \think\Exception( "accessToken必填！" );
		}
		
        $result = $this->verifyToken($token);
	    if( !empty($result['error'])  ){
	   		return $result;
	    }
		
	 	$result['info'] = model( $this->name )
	 			->with($this->withs)
				->where($this->key, $result['jwt']['userinfo'][$this->key] )
				->field($this->pwd_field,true)
				->find();
		
	    return $result;
    }
	
	
	/**
    * 创建accessToken
	* @is_need_login false
    * @param string $userInfo  用户信息
    * @param string $type  类型：  'access' 、'refresh'
    * @return josn token
    */
	public function getAccessToken($userId){
		return $this->createToken($userId,$this->accessTokenExpirationTime,'access');
	}
	
	/**
    * 创建刷新Token
	* @is_need_login false
    * @param string $userId  用户id
    * @return josn token
    */
	public function getRefreshToken($userId){
		return $this->createToken($userId,$this->refreshTokenExpirationTime,'refresh');
	}
	
	/**
     * 创建Token函数
	 * @is_need_login false
     * @param string/array  $info  额外信息
     * @return josn token
    */
    public function createToken($info,$exp,$type) {
        $token = [
            "iss"=>"",  	//签发者 可以为空
            "sub"=>"",    //jwt所面向的用户，可以为空
            "aud"=>"", 	//接收jwt的一方
            "iat" => time(), 		//签发时间
            "nbf" => time(), 		//在什么时候jwt开始生效 
            "exp" => time() + $exp, //token 过期时间
            "jti" => 1, 		//jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
            "token_type"=> $type,
        ];
		$token['userinfo'] = $info;
        return  JWT::encode($token,$this->strKey,"HS256"); //根据参数生成了 token
    }
	
	/**
     * 刷新token
	 * /sucai/soft/auth/refreshToken
	 * @is_need_login false
	 * @access public
     * @param string $refresh_token  刷新token
     * @return json
    */
	public function refreshToken(){
		$data = array("code"=>-10,"data"=>"","msg"=>"");
		
		$refresh_token = request()->post("refresh_token");
	 	if( empty($refresh_token) ){
			$data['msg'] = '未找到refresh_token！';
			return  json($data);
	 	}
		
		$userInfo = $this->getUserInfo($refresh_token);
		if( !empty($userInfo['error']) ){
			$data['msg'] = 'token过期，请重新登录！';
			return  json($data);
		}
		
		if( $userInfo['jwt']['token_type'] != 'refresh'){
			$data['msg'] = 'token类型错误！';
			return  json($data);
		}
		
		return $this->ReSucess($this->results($userInfo['info']),"token成功刷新!");
	} 
	
	/**
     * jws校验token
     *@is_need_login false
     * @param token
     * @return 返回 用户id
     * @throws JoseException
     */
    private function verifyToken($token) {
    	$result = ["error"=>"","jwt"=>""];
		try {
			$result["jwt"] =  JWT::decode($token,$this->strKey,["HS256"]) ;//解密jwt
			$result["jwt"] =  json_decode(json_encode($result["jwt"]) ,true);
		}catch (\Exception $e) {
            $result['error'] =  $e->getMessage();
        }
		return $result;
    } 
	
	/**
     * 返回接口
	 * /app/soft/login/refreshToken
	 * @access public
     * @param string $refresh_token  刷新token
     * @return json
    */
	public function results($userInfo){
		unset($userInfo['openid']);
		unset($userInfo['unionid']);
		$result = [
			"access_token" => $this->getAccessToken($userInfo),
			"refresh_token" => $this->getRefreshToken($userInfo),
			"userinfo" => $userInfo
		];
		return $result;
	}
	
}
